package fr.generali.accueilclient.gwt.rebind.place;

import java.io.PrintWriter;

import com.google.gwt.core.client.GWT;
import com.google.gwt.core.ext.Generator;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.place.shared.Place;
import com.google.gwt.place.shared.PlaceHistoryMapper;
import com.google.gwt.place.shared.PlaceTokenizer;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;

import fr.generali.accueilclient.gwt.client.place.impl.AbstractPlaceHistoryMapper;

/**
 * Generateur de code
 */
public class PlaceHistoryMapperGenerator extends Generator {

    private TreeLogger logger;

    @Override
    public String generate(TreeLogger logger, GeneratorContext generatorContext, String interfaceName)
                    throws UnableToCompleteException {

        this.logger = logger;

        PlaceHistoryGeneratorContext context =
                        PlaceHistoryGeneratorContextImpl.getInstance(logger, generatorContext.getTypeOracle(),
                                        interfaceName);

        PrintWriter out = generatorContext.tryCreate(logger, context.getPackageName(), context.getImplName());

        if (out != null) {
            generateOnce(generatorContext, context, out);
        }

        return context.getPackageName() + "." + context.getImplName();
    }

    private void generateOnce(GeneratorContext generatorContext, PlaceHistoryGeneratorContext context, PrintWriter out)
                    throws UnableToCompleteException {

        /* TreeLogger logger = */
        logger.branch(TreeLogger.DEBUG,
                        String.format("Generating implementation of %s", context.getInterfaceType().getName()));
        ClassSourceFileComposerFactory f =
                        new ClassSourceFileComposerFactory(context.getPackageName(), context.getImplName());

        f.addImport(Place.class.getCanonicalName());
        f.addImport(PlaceTokenizer.class.getCanonicalName());
        f.addImport(PlaceHistoryMapper.class.getCanonicalName());
        f.addImport(AbstractPlaceHistoryMapper.class.getCanonicalName());
        f.addImport(context.getInterfaceType().getQualifiedSourceName());
        f.addImport(GWT.class.getCanonicalName());

        f.setSuperclass(AbstractPlaceHistoryMapper.class.getSimpleName());
        f.addImplementedInterface(context.getInterfaceType().getName());

        SourceWriter sw = f.createSourceWriter(generatorContext, out);
        sw.println();

        // 1 - création de la méthode getPrefixAndToken(Place newPlace)
        writeGetPrefixAndToken(context, sw);
        sw.println();

        // 2 - génération de la méthode getTokenizer(String prefix)
        writeGetTokenizer(context, sw);
        sw.println();

        sw.outdent();
        sw.println("}");
        generatorContext.commit(logger, out);
    }

    /**
     * Exemple du code généré : protected PrefixAndToken getPrefixAndToken(Place
     * newPlace) { if (newPlace instanceof
     * fr.generali.accueilclient.web.client.view.ficheclient.FicheClientPlace) {
     * fr.generali.accueilclient.web.client.view.ficheclient.FicheClientPlace
     * place =
     * (fr.generali.accueilclient.web.client.view.ficheclient.FicheClientPlace)
     * newPlace;
     * PlaceTokenizer<fr.generali.accueilclient.web.client.view.ficheclient
     * .FicheClientPlace> t =
     * GWT.create(fr.generali.accueilclient.web.client.view
     * .ficheclient.FicheClientPlace.Tokenizer.class); return new
     * PrefixAndToken("ficheClient",
     * t.getToken((fr.generali.accueilclient.web.client
     * .view.ficheclient.FicheClientPlace) place)); } }
     */

    private void writeGetPrefixAndToken(PlaceHistoryGeneratorContext context, SourceWriter sw)
                    throws UnableToCompleteException {
        sw.println("protected PrefixAndToken getPrefixAndToken(Place newPlace) {");
        sw.indent();
        for (JClassType placeType : context.getPlaceTypes()) {

            String placeTypeName = placeType.getQualifiedSourceName();
            String prefix = context.getPrefix(placeType);

            sw.println("if (newPlace instanceof " + placeTypeName + ") {");
            sw.indent();
            sw.println(placeTypeName + " place = (" + placeTypeName + ") newPlace;");
            String newPrefix = prefix;
            JMethod getter = context.getTokenizerGetter(prefix);
            if (getter != null) {
                sw.println(String.format("return new PrefixAndToken(\"%s\", " + "factory.%s().getToken(place));",// ,
                                                                                                                 // %b

                                escape(newPrefix), getter.getName()));
            } else {
                sw.println(String.format("PlaceTokenizer<%s> t = GWT.create(%s.class);", placeTypeName, context
                                .getTokenizerType(prefix).getQualifiedSourceName()));
                sw.println(String.format("return new PrefixAndToken(\"%s\", " + "t.getToken((%s) place));", // ,
                                                                                                            // %b
                                escape(newPrefix), placeTypeName));
            }

            sw.outdent();
            sw.println("}");
        }

        sw.println("return null;");
        sw.outdent();
        sw.println("}");
    }

    /**
     * Exemple de code généré : protected PlaceTokenizer<?> getTokenizer(String
     * prefix) { if ("listeDetailOpportunites".equals(prefix)) { return
     * GWT.create
     * (fr.generali.accueilclient.web.client.view.listedetailopportunites
     * .ListeDetailOpportunitesPlace.Tokenizer.class); } if
     * ("creationProspect".equals(prefix)) { return
     * GWT.create(fr.generali.accueilclient
     * .web.client.view.prospect.ProspectCreationPlace.Tokenizer.class); }
     * return null }
     **/

    private void writeGetTokenizer(PlaceHistoryGeneratorContext context, SourceWriter sw)
                    throws UnableToCompleteException {
        sw.println("protected PlaceTokenizer<?> getTokenizer(String prefix) {");
        sw.indent();

        for (String prefix : context.getPrefixes()) {
            JMethod getter = context.getTokenizerGetter(prefix);
            String newPrefix = prefix;
            if (prefix.charAt(0) == '!') {
                newPrefix = prefix.substring(1);
            }
            sw.println("if (\"" + escape(newPrefix) + "\".equals(prefix)) {");
            sw.indent();

            if (getter != null) {
                sw.println("return factory." + getter.getName() + "();");
            } else {
                sw.println(String.format("return GWT.create(%s.class);", context.getTokenizerType(prefix)
                                .getQualifiedSourceName()));
            }

            sw.outdent();
            sw.println("}");
        }

        sw.println("return null;");
        sw.outdent();
        sw.println("}");
        sw.outdent();
    }
}
